package cmux

import (
	"bytes"
	"io"
)

// 定义了一个名为 bufferedReader 的类型，它是一个 io.Reader 接口的优化实现
// 它特别用于从两个数据源（一个内存缓冲区和一个外部数据源）中读取数据，同时避免不必要的内存分配
type bufferedReader struct {
	source     io.Reader    // 原始数据源
	buffer     bytes.Buffer // 缓存数据的缓冲区
	bufferRead int          // 记录从 buffer 中已经读取的字节数
	bufferSize int          // 缓冲区的总大小（即当前存储在 buffer 中的字节数）
	sniffing   bool         // 一个布尔值，表示当前是否处于“嗅探”模式。在嗅探模式下，从 source 读取的数据会被写入 buffer 中，但不返回给调用者
	lastErr    error        // 上一次从 source 读取时遇到的错误（如果有）
}

// Read 方法是 io.Reader 接口的一部分，用于从 bufferedReader 中读取数据
/*
	1 从缓冲区读取：
	- 如果缓冲区中有未读取的数据（即 bufferSize > bufferRead），则直接从缓冲区中读取数据并返回。
	- 返回的数据量由 p 的大小限制。
	- 如果之前从 source 读取时遇到错误，则返回该错误。

	2 重置缓冲区（如果需要）：
	- 如果当前不在嗅探模式，并且缓冲区有容量（即之前使用过），则重置缓冲区以释放其内部切片。

	3 从数据源读取：
	- 如果缓冲区中没有数据，则从 source 中读取数据到 p 中。
	- 如果在嗅探模式下，将读取的数据也写入到 buffer 中。
	- 返回从 source 读取的字节数和遇到的任何错误。
*/
func (s *bufferedReader) Read(p []byte) (int, error) {
	if s.bufferSize > s.bufferRead {
		bn := copy(p, s.buffer.Bytes()[s.bufferRead:s.bufferSize])
		s.bufferRead += bn
		return bn, s.lastErr
	} else if !s.sniffing && s.buffer.Cap() != 0 {
		s.buffer = bytes.Buffer{}
	}

	sn, sErr := s.source.Read(p)
	if sn > 0 && s.sniffing {
		s.lastErr = sErr
		if wn, wErr := s.buffer.Write(p[:sn]); wErr != nil {
			return wn, wErr
		}
	}
	return sn, sErr
}

// reset 方法用于重置 bufferedReader 的状态，特别是嗅探模式和相关的读取计数
func (s *bufferedReader) reset(snif bool) {
	s.sniffing = snif
	s.bufferRead = 0
	s.bufferSize = s.buffer.Len()
}

/*
	工作原理
		这个bufferedReader结构体在读取数据时，首先尝试从缓冲区中读取数据。
		如果缓冲区中没有数据或数据已经被读取完，它会从原始数据源source中读取数据。
		在嗅探模式下，从source中读取的数据会被同时写入到缓冲区中，以便后续使用。

		这种设计在需要“预览”或“嗅探”数据流的一部分内容的场景中特别有用，
		因为你可以在读取并处理一部分数据的同时，保留另一部分数据以供后续使用，而无需再次从原始数据源中读取。


	使用示例
		你可能会在HTTP/2的服务器实现中看到类似的读取器，其中服务器需要解析HTTP/1.1和HTTP/2的帧，但不确定传入的数据流是哪种协议。
		通过使用bufferedReader，服务器可以在不阻塞原始数据源的情况下“嗅探”数据，以确定应该使用哪种协议解析器。
*/
